<?php

abstract class GrammarParser 
{   
	/**
	 * Arbititrary token to denote the end of the expression.
	 */
	const END_CHAR = '*';		
	
	/**
	 * GrammarParser Constructor
	 * 
	 * @param string expression
	 */
	public final function __construct($expression)
	{		
	 	$this->_index = 0;
	 	$tokens = array_merge($this->_getBinaryOperators(), $this->_getGroupingTokens(), $this->_getUnaryOperators());
	 	
	 	$this->_expressionTokens = $this->_prepareExpression($expression, $tokens);
	}
	
	protected abstract function _getBinaryOperators();
	protected abstract function _getUnaryOperators();
	protected abstract function _getGroupingTokens();	
	
	/**
	 * Convenience method to parse the expression into a token array.
	 * 
	 * @param string expression
	 * @return array token array
	 */
	protected function _prepareExpression($expression, array $tokens)
	{
		$replace = $tokens;	
		
		foreach($replace as $token){
			$replaceWith[] = ' ' . $token . ' ';
		}
		
		$tokenArray = explode(' ', str_replace($replace, $replaceWith, $expression));
		
		$retArray = array();
		foreach($tokenArray as $token){
			$token = trim($token);
			if($token != null){
				$retArray[] = trim($token);
			}
		}
		
		$retArray[] = GrammarParser::END_CHAR;
		
		return $retArray;
	}   
	
	/**
	 * Gets next token in expression.
	 * 
	 * @return string token
	 */
	protected function _next()
	{
		return $this->_expressionTokens[$this->_index];
	}	
	
	/**
	 * Expects particular token, if token is not received, throws
	 * Invalid token exception.
	 * 
	 * @param string token.
	 * @throws Exception invalid token.
	 */
	protected function _expect($token)
	{
		if ($this->_next() == $token) {
			$this->_consume();
		} else
			throw new Exception('Invalid Token Exception expecting ' . $token . ' encountered ' . $this->_next());	
	} 
	
	/**
	 * Consumes next token.
	 * 
	 * @return void
	 */
	protected function _consume()
	{
	 	++ $this->_index;
	}
	
	protected function _isBinary($token)
	{
	 	$ops = $this->_getBinaryOperators();
	 	return in_array($token, $ops);
	}
	 
	protected function _isUnary($token)
	{
	 	$ops = $this->_getUnaryOperators();
	 	return in_array($token, $ops);
	}
	
	protected function _isGroupingToken($token)
	{
	 	$ops = $this->_getGroupingTokens();
	 	return in_array($token, $ops);
	}	
	
	protected function _isV($token)
	{
		return !$this->_isGroupingToken($token) && !$this->_isBinary($token) && !$this->_isUnary($token);
	}	
	
}
?>